// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';

import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
import 'package:analysis_server/src/legacy_analysis_server.dart';
import 'package:analysis_server/src/plugin/result_merger.dart';
import 'package:analysis_server/src/request_handler_mixin.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
import 'package:analyzer_plugin/src/utilities/navigation/navigation_dart.dart';

/// The handler for the `analysis.getNavigation` request.
class AnalysisGetNavigationHandler extends LegacyHandler
    with RequestHandlerMixin<LegacyAnalysisServer> {
  /// Initialize a newly created handler to be able to service requests for the
  /// [server].
  AnalysisGetNavigationHandler(
    super.server,
    super.request,
    super.cancellationToken,
    super.performance,
  );

  @override
  Future<void> handle() async {
    var params = AnalysisGetNavigationParams.fromRequest(
      request,
      clientUriConverter: server.uriConverter,
    );
    var file = params.file;
    var offset = params.offset;
    var length = params.length;

    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
      return;
    }

    var driver = server.getAnalysisDriver(file);
    if (driver == null) {
      sendResponse(Response.getNavigationInvalidFile(request));
    } else {
      //
      // Allow plugins to start computing navigation data.
      //
      var requestParams = plugin.AnalysisGetNavigationParams(
        file,
        offset,
        length,
      );
      var pluginFutures = server.broadcastRequestToPlugins(
        requestParams,
        driver,
      );
      //
      // Compute navigation data generated by server.
      //
      var allResults = <AnalysisNavigationParams>[];
      var result = await server.getResolvedUnit(file);
      if (result != null) {
        var collector = NavigationCollectorImpl();
        computeDartNavigation(
          server.resourceProvider,
          collector,
          result,
          offset,
          length,
        );
        collector.createRegions();
        allResults.add(
          AnalysisNavigationParams(
            file,
            collector.regions,
            collector.targets,
            collector.files,
          ),
        );
      }
      //
      // Add the navigation data produced by plugins to the server-generated
      // navigation data.
      //
      var responses = await waitForResponses(
        pluginFutures,
        requestParameters: requestParams,
      );
      for (var response in responses) {
        var result = plugin.AnalysisGetNavigationResult.fromResponse(response);
        allResults.add(
          AnalysisNavigationParams(
            file,
            result.regions,
            result.targets,
            result.files,
          ),
        );
      }
      //
      // Return the result.
      //
      var merger = ResultMerger();
      var mergedResults = merger.mergeNavigation(allResults);
      if (mergedResults == null) {
        sendResult(
          AnalysisGetNavigationResult(
            <String>[],
            <NavigationTarget>[],
            <NavigationRegion>[],
          ),
        );
      } else {
        sendResult(
          AnalysisGetNavigationResult(
            mergedResults.files,
            mergedResults.targets,
            mergedResults.regions,
          ),
        );
      }
    }
  }
}
